#include "Zookit.h"
#include <iostream>
#include <string.h>

namespace SpiderWebKitClient {

static void zktest_watcher_g(zhandle_t* zh, int type, int state,
        const char* path, void* watcherCtx)
{
    /*
    printf("Something happened.\n");
    printf("type: %d\n", type);
    printf("state: %d\n", state);
    printf("path: %s\n", path);
    printf("watcherCtx: %s\n", (char *)watcherCtx);
    */
}

ZooKit::ZooKit()
    : m_handle(NULL)
    , m_root("/spider_v2/")
{
    zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
}

ZooKit::~ZooKit()
{
    zookeeper_close(m_handle);
}

void ZooKit::connect(std::string host)
{
    m_host = host;
    m_handle = zookeeper_init(host.c_str(), zktest_watcher_g, 30000, 0, (void*)"spider webkit core", 0);

    if (m_handle == NULL) {
        fprintf(stderr, "Error when connecting to zookeeper servers...\n");
        exit(EXIT_FAILURE);
    }
}

void ZooKit::set(std::string path, std::string content)
{
    int ret = zoo_set(m_handle, path.c_str(), content.data(), content.size(), -1);
    if (ret)
        fprintf(stderr, "Error %d for %s path %s\n", ret, "set", path.c_str());

    if (ret == ZINVALIDSTATE || ret == ZNONODE) {
        zookeeper_close(m_handle);
        connect(m_host);
    }
}

bool ZooKit::get(std::string group, std::string appip, ProcessRouter::ManagerInfoStruct &is, int retry)
{
    std::string content;
    std::string path = m_root + group + "/" + appip + "/stateinfo";
    char buffer[2048] = {0};
    int len = 0;
    int zretry = 100;

    struct Stat stat;

    int ret = 0;
    do {
        ret = zoo_exists(m_handle, path.c_str(), 0, &stat);
    } while(ret != ZOK && ret != ZNONODE && zretry-- > 0);

    if (ret == ZNONODE)
        return false;

    if (ret != ZOK)
        return false;

    zretry = 100;

    const int BUF_LEN = 10 * 1024;
    char buf[BUF_LEN] = {0};
    len = BUF_LEN - 1;
    memset(&stat, 0, sizeof(struct Stat));
    int rc = ZOK;
    do {
        do {
            len = BUF_LEN - 1;
            rc = zoo_get(m_handle, path.c_str(), 0, buf, &len, &stat);
        } while (rc != ZOK && retryFeasible(rc) && zretry-- > 0);
        int tzr = zretry;
        zretry = 100;

        if (rc != ZOK) {
            std::cout << "Failed fetching data: " << zerror(rc) << std::endl;
            continue;
        } else {
            if (len == stat.dataLength) {
                content.append(buf, len);
                if (is.ParseFromString(content)) {
                    return true;
                }
            }
        }
    } while (retry-- > 0);
    return false;
}

bool ZooKit::getChild(std::string group, std::list<std::string> &childs, int retry)
{
    std::string path = m_root + group;
    struct String_vector strings;
    memset(&strings, 0, sizeof(struct String_vector));
    int rc = ZOK;
    do {
        rc = zoo_get_children(m_handle, path.c_str(), 0, &strings);
    } while (rc != ZOK && retryFeasible(rc) && retry-- > 0);
    if (rc != ZOK) {
        std::cout << "Failed fetching data: " << zerror(rc) << std::endl;
        return false;
    } else {
        for (int i = 0; i < strings.count; i ++) {
            childs.push_back(strings.data[i]);
        }
        return true;
    }
}

bool ZooKit::retryFeasible(int zkrc) {
    if (zkrc == ZDATAINCONSISTENCY)
        std::cout << __func__ << " ZDATAINCONSISTENCY" << std::endl;
    if (zkrc == ZCONNECTIONLOSS
            || zkrc == ZMARSHALLINGERROR
            || zkrc == ZOPERATIONTIMEOUT
            || zkrc == ZDATAINCONSISTENCY)
        return true;
    else
        return false;
}

}

